home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / share / iceweasel / modules / InsideOutBox.jsm < prev    next >
Encoding:
Text File  |  2013-01-09  |  18.8 KB  |  664 lines

  1. /*
  2.  * Software License Agreement (BSD License)
  3.  *
  4.  * Copyright (c) 2007, Parakey Inc.
  5.  * All rights reserved.
  6.  * 
  7.  * Redistribution and use of this software in source and binary forms, with or without modification,
  8.  * are permitted provided that the following conditions are met:
  9.  * 
  10.  * * Redistributions of source code must retain the above
  11.  *   copyright notice, this list of conditions and the
  12.  *   following disclaimer.
  13.  * 
  14.  * * Redistributions in binary form must reproduce the above
  15.  *   copyright notice, this list of conditions and the
  16.  *   following disclaimer in the documentation and/or other
  17.  *   materials provided with the distribution.
  18.  * 
  19.  * * Neither the name of Parakey Inc. nor the names of its
  20.  *   contributors may be used to endorse or promote products
  21.  *   derived from this software without specific prior
  22.  *   written permission of Parakey Inc.
  23.  * 
  24.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
  25.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  26.  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  27.  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  28.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  29.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
  30.  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  31.  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32.  */
  33.  
  34. /*
  35.  * Creator:
  36.  *  Joe Hewitt
  37.  * Contributors
  38.  *  John J. Barton (IBM Almaden)
  39.  *  Jan Odvarko (Mozilla Corp.)
  40.  *  Max Stepanov (Aptana Inc.)
  41.  *  Rob Campbell (Mozilla Corp.)
  42.  *  Hans Hillen (Paciello Group, Mozilla)
  43.  *  Curtis Bartley (Mozilla Corp.)
  44.  *  Mike Collins (IBM Almaden)
  45.  *  Kevin Decker
  46.  *  Mike Ratcliffe (Comartis AG)
  47.  *  Hernan Rodr├¡guez Colmeiro
  48.  *  Austin Andrews
  49.  *  Christoph Dorn
  50.  *  Steven Roussey (AppCenter Inc, Network54)
  51.  */
  52.  
  53. ///////////////////////////////////////////////////////////////////////////
  54. //// InsideOutBox
  55.  
  56. /**
  57.  * InsideOutBoxView is a simple interface definition for views implementing
  58.  * InsideOutBox controls. All implementors must define these methods.
  59.  * Implemented in InspectorUI.
  60.  */
  61.  
  62. /*
  63. InsideOutBoxView = {
  64.   //
  65.    * Retrieves the parent object for a given child object.
  66.    * @param aChild
  67.    *        The child node to retrieve the parent object for.
  68.    * @returns a DOM node | null
  69.    //
  70.   getParentObject: function(aChild) {},
  71.  
  72.   //
  73.    * Retrieves a given child node.
  74.    *
  75.    * If both index and previousSibling are passed, the implementation
  76.    * may assume that previousSibling will be the return for getChildObject
  77.    * with index-1.
  78.    * @param aParent
  79.    *        The parent object of the child object to retrieve.
  80.    * @param aIndex
  81.    *        The index of the child object to retrieve from aParent.
  82.    * @param aPreviousSibling
  83.    *        The previous sibling of the child object to retrieve.
  84.    *        Supercedes aIndex.
  85.    * @returns a DOM object | null
  86.    //
  87.   getChildObject: function(aParent, aIndex, aPreviousSibling) {},
  88.  
  89.   //
  90.    * Renders the HTML representation of the object. Should return an HTML
  91.    * object which will be displayed to the user.
  92.    * @param aObject
  93.    *        The object to create the box object for.
  94.    * @param aIsRoot
  95.    *        Is the object the root object. May not be used in all
  96.    *        implementations.
  97.    * @returns an object box | null
  98.    //
  99.   createObjectBox: function(aObject, aIsRoot) {},
  100.  
  101.   //
  102.   * Convenience wrappers for classList API.
  103.   * @param aObject
  104.   *        DOM node to query/set.
  105.   * @param aClassName
  106.   *        String containing the class name to query/set.
  107.   //
  108.   hasClass: function(aObject, aClassName) {},
  109.   addClass: function(aObject, aClassName) {},
  110.   removeClass: function(aObject, aClassName) {}
  111. };
  112. */
  113.  
  114. /**
  115.  * Creates a tree based on objects provided by a separate "view" object.
  116.  *
  117.  * Construction uses an "inside-out" algorithm, meaning that the view's job is
  118.  * first to tell us the ancestry of each object, and secondarily its
  119.  * descendants.
  120.  *
  121.  * Constructor
  122.  * @param aView
  123.  *        The view requiring the InsideOutBox.
  124.  * @param aBox
  125.  *        The box object containing the InsideOutBox. Required to add/remove
  126.  *        children during box manipulation (toggling opened or closed).
  127.  */
  128.  
  129. var EXPORTED_SYMBOLS = ["InsideOutBox"];
  130.  
  131. function InsideOutBox(aView, aBox)
  132. {
  133.   this.view = aView;
  134.   this.box = aBox;
  135.  
  136.   this.rootObject = null;
  137.  
  138.   this.rootObjectBox = null;
  139.   this.selectedObjectBox = null;
  140.   this.highlightedObjectBox = null;
  141.   this.scrollIntoView = false;
  142. };
  143.  
  144. InsideOutBox.prototype =
  145. {
  146.   /**
  147.    * Highlight the given object node in the tree.
  148.    * @param aObject
  149.    *        the object to highlight.
  150.    * @returns objectBox
  151.    */
  152.   highlight: function IOBox_highlight(aObject)
  153.   {
  154.     let objectBox = this.createObjectBox(aObject);
  155.     this.highlightObjectBox(objectBox);
  156.     return objectBox;
  157.   },
  158.  
  159.   /**
  160.    * Open the given object node in the tree.
  161.    * @param aObject
  162.    *        The object node to open.
  163.    * @returns objectBox
  164.    */
  165.   openObject: function IOBox_openObject(aObject)
  166.   {
  167.     let object = aObject;
  168.     let firstChild = this.view.getChildObject(object, 0);
  169.     if (firstChild)
  170.       object = firstChild;
  171.  
  172.     return this.openToObject(object);
  173.   },
  174.  
  175.   /**
  176.    * Open the tree up to the given object node.
  177.    * @param aObject
  178.    *        The object in the tree to open to.
  179.    * @returns objectBox
  180.    */
  181.   openToObject: function IOBox_openToObject(aObject)
  182.   {
  183.     let objectBox = this.createObjectBox(aObject);
  184.     this.openObjectBox(objectBox);
  185.     return objectBox;
  186.   },
  187.  
  188.   /**
  189.    * Select the given object node in the tree.
  190.    * @param aObject
  191.    *        The object node to select.
  192.    * @param makeBoxVisible
  193.    *        Boolean. Open the object box in the tree?
  194.    * @param forceOpen
  195.    *        Force the object box open by expanding all elements in the tree?
  196.    * @param scrollIntoView
  197.    *        Scroll the objectBox into view?
  198.    * @returns nsIDOMNode|null
  199.    *          A DOM node that represents the "object box", the element that
  200.    *          holds/displays the given aObject representation in the tree. If
  201.    *          the object cannot be selected, if it is a stale object, null is
  202.    *          returned.
  203.    */
  204.   select:
  205.   function IOBox_select(aObject, makeBoxVisible, forceOpen, scrollIntoView)
  206.   {
  207.     let objectBox = this.createObjectBox(aObject);
  208.     if (!objectBox) {
  209.       return null;
  210.     }
  211.     this.selectObjectBox(objectBox, forceOpen);
  212.     if (makeBoxVisible) {
  213.       this.openObjectBox(objectBox);
  214.       if (scrollIntoView) {
  215.         objectBox.scrollIntoView(true);
  216.       }
  217.     }
  218.     return objectBox;
  219.   },
  220.  
  221.   /**
  222.    * Expands/contracts the given object, depending on its state.
  223.    * @param aObject
  224.    *        The tree node to expand/contract.
  225.    */
  226.   toggleObject: function IOBox_toggleObject(aObject)
  227.   {
  228.     let box = this.createObjectBox(aObject);
  229.     if (!(this.view.hasClass(box, "open")))
  230.       this.expandObjectBox(box);
  231.     else
  232.       this.contractObjectBox(box);
  233.   },
  234.  
  235.   /**
  236.    * Expand the given object in the tree.
  237.    * @param aObject
  238.    *        The tree node to expand.
  239.    */
  240.   expandObject: function IOBox_expandObject(aObject)
  241.   {
  242.     let objectBox = this.createObjectBox(aObject);
  243.     if (objectBox)
  244.       this.expandObjectBox(objectBox);
  245.   },
  246.  
  247.   /**
  248.    * Contract the given object in the tree.
  249.    * @param aObject
  250.    *        The tree node to contract.
  251.    */
  252.   contractObject: function IOBox_contractObject(aObject)
  253.   {
  254.     let objectBox = this.createObjectBox(aObject);
  255.     if (objectBox)
  256.       this.contractObjectBox(objectBox);
  257.   },
  258.  
  259.   /**
  260.    * General method for iterating over an object's ancestors and performing
  261.    * some function.
  262.    * @param aObject
  263.    *        The object whose ancestors we wish to iterate over.
  264.    * @param aCallback
  265.    *        The function to call with the object as argument.
  266.    */
  267.  
  268.   iterateObjectAncestors: function IOBox_iterateObjectAncesors(aObject, aCallback)
  269.   {
  270.     let object = aObject;
  271.     if (!(aCallback && typeof(aCallback) == "function")) {
  272.       this.view._log("Illegal argument in IOBox.iterateObjectAncestors");
  273.       return;
  274.     }
  275.     while ((object = this.getParentObjectBox(object)))
  276.       aCallback(object);
  277.   },
  278.  
  279.   /**
  280.    * Highlight the given objectBox in the tree.
  281.    * @param aObjectBox
  282.    *        The objectBox to highlight.
  283.    */
  284.   highlightObjectBox: function IOBox_highlightObjectBox(aObjectBox)
  285.   {
  286.     let self = this;
  287.  
  288.     if (!aObjectBox)
  289.       return;
  290.  
  291.     if (this.highlightedObjectBox) {
  292.       this.view.removeClass(this.highlightedObjectBox, "highlighted");
  293.       this.iterateObjectAncestors(this.highlightedObjectBox, function (box) {
  294.         self.view.removeClass(box, "highlightOpen");
  295.       });
  296.     }
  297.  
  298.     this.highlightedObjectBox = aObjectBox;
  299.  
  300.     this.view.addClass(aObjectBox, "highlighted");
  301.     this.iterateObjectAncestors(this.highlightedObjectBox, function (box) {
  302.       self.view.addClass(box, "highlightOpen");
  303.     });
  304.  
  305.     aObjectBox.scrollIntoView(true);
  306.   },
  307.  
  308.   /**
  309.    * Select the given objectBox in the tree, forcing it to be open if necessary.
  310.    * @param aObjectBox
  311.    *        The objectBox to select.
  312.    * @param forceOpen
  313.    *        Force the box (subtree) to be open?
  314.    */
  315.   selectObjectBox: function IOBox_selectObjectBox(aObjectBox, forceOpen)
  316.   {
  317.     let isSelected = this.selectedObjectBox &&
  318.       aObjectBox == this.selectedObjectBox;
  319.  
  320.     // aObjectBox is already selected, return
  321.     if (isSelected)
  322.       return;
  323.  
  324.     if (this.selectedObjectBox)
  325.       this.view.removeClass(this.selectedObjectBox, "selected");
  326.  
  327.     this.selectedObjectBox = aObjectBox;
  328.  
  329.     if (aObjectBox) {
  330.       this.view.addClass(aObjectBox, "selected");
  331.  
  332.       // Force it open the first time it is selected
  333.       if (forceOpen)
  334.         this.expandObjectBox(aObjectBox, true);
  335.     }
  336.   },
  337.  
  338.   /**
  339.    * Open the ancestors of the given object box.
  340.    * @param aObjectBox
  341.    *        The object box to open.
  342.    */
  343.   openObjectBox: function IOBox_openObjectBox(aObjectBox)
  344.   {
  345.     if (!aObjectBox)
  346.       return;
  347.  
  348.     let self = this;
  349.     this.iterateObjectAncestors(aObjectBox, function (box) {
  350.       self.view.addClass(box, "open");
  351.       let labelBox = box.querySelector(".nodeLabelBox");
  352.       if (labelBox)
  353.         labelBox.setAttribute("aria-expanded", "true");
  354.    });
  355.   },
  356.  
  357.   /**
  358.    * Expand the given object box.
  359.    * @param aObjectBox
  360.    *        The object box to expand.
  361.    */
  362.   expandObjectBox: function IOBox_expandObjectBox(aObjectBox)
  363.   {
  364.     let nodeChildBox = this.getChildObjectBox(aObjectBox);
  365.  
  366.     // no children means nothing to expand, return
  367.     if (!nodeChildBox)
  368.       return;
  369.  
  370.     if (!aObjectBox.populated) {
  371.       let firstChild = this.view.getChildObject(aObjectBox.repObject, 0);
  372.       this.populateChildBox(firstChild, nodeChildBox);
  373.     }
  374.     let labelBox = aObjectBox.querySelector(".nodeLabelBox");
  375.     if (labelBox)
  376.       labelBox.setAttribute("aria-expanded", "true");
  377.     this.view.addClass(aObjectBox, "open");
  378.   },
  379.  
  380.   /**
  381.    * Contract the given object box.
  382.    * @param aObjectBox
  383.    *        The object box to contract.
  384.    */
  385.   contractObjectBox: function IOBox_contractObjectBox(aObjectBox)
  386.   {
  387.     this.view.removeClass(aObjectBox, "open");
  388.     let nodeLabel = aObjectBox.querySelector(".nodeLabel");
  389.     let labelBox = nodeLabel.querySelector(".nodeLabelBox");
  390.     if (labelBox)
  391.       labelBox.setAttribute("aria-expanded", "false");
  392.   },
  393.  
  394.   /**
  395.    * Toggle the given object box, forcing open if requested.
  396.    * @param aObjectBox
  397.    *        The object box to toggle.
  398.    * @param forceOpen
  399.    *        Force the objectbox open?
  400.    */
  401.   toggleObjectBox: function IOBox_toggleObjectBox(aObjectBox, forceOpen)
  402.   {
  403.     let isOpen = this.view.hasClass(aObjectBox, "open");
  404.  
  405.     if (!forceOpen && isOpen)
  406.       this.contractObjectBox(aObjectBox);
  407.     else if (!isOpen)
  408.       this.expandObjectBox(aObjectBox);
  409.   },
  410.  
  411.   /**
  412.    * Creates all of the boxes for an object, its ancestors, and siblings.
  413.    * @param aObject
  414.    *        The tree node to create the object boxes for.
  415.    * @returns anObjectBox or null
  416.    */
  417.   createObjectBox: function IOBox_createObjectBox(aObject)
  418.   {
  419.     if (!aObject)
  420.       return null;
  421.  
  422.     this.rootObject = this.getRootNode(aObject) || aObject;
  423.  
  424.     // Get or create all of the boxes for the target and its ancestors
  425.     let objectBox = this.createObjectBoxes(aObject, this.rootObject);
  426.  
  427.     if (!objectBox)
  428.       return null;
  429.  
  430.     if (aObject == this.rootObject)
  431.       return objectBox;
  432.  
  433.     return this.populateChildBox(aObject, objectBox.parentNode);
  434.   },
  435.  
  436.   /**
  437.    * Creates all of the boxes for an object, its ancestors, and siblings up to
  438.    * a root.
  439.    * @param aObject
  440.    *        The tree's object node to create the object boxes for.
  441.    * @param aRootObject
  442.    *        The root object at which to stop building object boxes.
  443.    * @returns an object box or null
  444.    */
  445.   createObjectBoxes: function IOBox_createObjectBoxes(aObject, aRootObject)
  446.   {
  447.     if (!aObject)
  448.       return null;
  449.  
  450.     if (aObject == aRootObject) {
  451.       if (!this.rootObjectBox || this.rootObjectBox.repObject != aRootObject) {
  452.         if (this.rootObjectBox) {
  453.           try {
  454.             this.box.removeChild(this.rootObjectBox);
  455.           } catch (exc) {
  456.             this.view._log("this.box.removeChild(this.rootObjectBox) FAILS " +
  457.               this.box + " must not contain " + this.rootObjectBox);
  458.           }
  459.         }
  460.  
  461.         this.highlightedObjectBox = null;
  462.         this.selectedObjectBox = null;
  463.         this.rootObjectBox = this.view.createObjectBox(aObject, true);
  464.         this.box.appendChild(this.rootObjectBox);
  465.       }
  466.       return this.rootObjectBox;
  467.     }
  468.  
  469.     let parentNode = this.view.getParentObject(aObject);
  470.     let parentObjectBox = this.createObjectBoxes(parentNode, aRootObject);
  471.  
  472.     if (!parentObjectBox)
  473.       return null;
  474.  
  475.     let parentChildBox = this.getChildObjectBox(parentObjectBox);
  476.  
  477.     if (!parentChildBox)
  478.       return null;
  479.  
  480.     let childObjectBox = this.findChildObjectBox(parentChildBox, aObject);
  481.  
  482.     return childObjectBox ? childObjectBox
  483.       : this.populateChildBox(aObject, parentChildBox);
  484.   },
  485.  
  486.   /**
  487.    * Locate the object box for a given object node.
  488.    * @param aObject
  489.    *        The given object node in the tree.
  490.    * @returns an object box or null.
  491.    */
  492.   findObjectBox: function IOBox_findObjectBox(aObject)
  493.   {
  494.     if (!aObject)
  495.       return null;
  496.  
  497.     if (aObject == this.rootObject)
  498.       return this.rootObjectBox;
  499.  
  500.     let parentNode = this.view.getParentObject(aObject);
  501.     let parentObjectBox = this.findObjectBox(parentNode);
  502.     if (!parentObjectBox)
  503.       return null;
  504.  
  505.     let parentChildBox = this.getChildObjectBox(parentObjectBox);
  506.     if (!parentChildBox)
  507.       return null;
  508.  
  509.     return this.findChildObjectBox(parentChildBox, aObject);
  510.   },
  511.  
  512.   getAncestorByClass: function IOBox_getAncestorByClass(node, className)
  513.   {
  514.     for (let parent = node; parent; parent = parent.parentNode) {
  515.       if (this.view.hasClass(parent, className))
  516.         return parent;
  517.     }
  518.  
  519.     return null;
  520.   },
  521.  
  522.   /**
  523.    * We want all children of the parent of repObject.
  524.    */
  525.   populateChildBox: function IOBox_populateChildBox(repObject, nodeChildBox)
  526.   {
  527.     if (!repObject)
  528.       return null;
  529.  
  530.     let parentObjectBox = this.getAncestorByClass(nodeChildBox, "nodeBox");
  531.  
  532.     if (parentObjectBox.populated)
  533.       return this.findChildObjectBox(nodeChildBox, repObject);
  534.  
  535.     let lastSiblingBox = this.getChildObjectBox(nodeChildBox);
  536.     let siblingBox = nodeChildBox.firstChild;
  537.     let targetBox = null;
  538.     let view = this.view;
  539.     let targetSibling = null;
  540.     let parentNode = view.getParentObject(repObject);
  541.  
  542.     for (let i = 0; 1; ++i) {
  543.       targetSibling = view.getChildObject(parentNode, i, targetSibling);
  544.       if (!targetSibling)
  545.         break;
  546.  
  547.       // Check if we need to start appending, or continue to insert before
  548.       if (lastSiblingBox && lastSiblingBox.repObject == targetSibling)
  549.         lastSiblingBox = null;
  550.  
  551.       if (!siblingBox || siblingBox.repObject != targetSibling) {
  552.         let newBox = view.createObjectBox(targetSibling);
  553.         if (newBox) {
  554.           if (lastSiblingBox)
  555.             nodeChildBox.insertBefore(newBox, lastSiblingBox);
  556.           else
  557.             nodeChildBox.appendChild(newBox);
  558.         }
  559.  
  560.         siblingBox = newBox;
  561.       }
  562.  
  563.       if (targetSibling == repObject)
  564.         targetBox = siblingBox;
  565.  
  566.       if (siblingBox && siblingBox.repObject == targetSibling)
  567.         siblingBox = siblingBox.nextSibling;
  568.     }
  569.  
  570.     if (targetBox)
  571.       parentObjectBox.populated = true;
  572.  
  573.     return targetBox;
  574.   },
  575.  
  576.   /**
  577.    * Get the parent object box of a given object box.
  578.    * @params aObjectBox
  579.    *         The object box of the parent.
  580.    * @returns an object box or null
  581.    */
  582.   getParentObjectBox: function IOBox_getParentObjectBox(aObjectBox)
  583.   {
  584.     let parent = aObjectBox.parentNode ? aObjectBox.parentNode.parentNode : null;
  585.     return parent && parent.repObject ? parent : null;
  586.   },
  587.  
  588.   /**
  589.    * Get the child object box of a given object box.
  590.    * @param aObjectBox
  591.    *        The object box whose child you want.
  592.    * @returns an object box or null
  593.    */
  594.   getChildObjectBox: function IOBox_getChildObjectBox(aObjectBox)
  595.   {
  596.     return aObjectBox.querySelector(".nodeChildBox");
  597.   },
  598.  
  599.   /**
  600.    * Find the child object box for a given repObject within the subtree
  601.    * rooted at aParentNodeBox.
  602.    * @param aParentNodeBox
  603.    *        root of the subtree in which to search for repObject.
  604.    * @param aRepObject
  605.    *        The object you wish to locate in the subtree.
  606.    * @returns an object box or null
  607.    */
  608.   findChildObjectBox: function IOBox_findChildObjectBox(aParentNodeBox, aRepObject)
  609.   {
  610.     let childBox = aParentNodeBox.firstChild;
  611.     while (childBox) {
  612.       if (childBox.repObject == aRepObject)
  613.         return childBox;
  614.       childBox = childBox.nextSibling;
  615.     }
  616.     return null; // not found
  617.   },
  618.  
  619.   /**
  620.    * Determines if the given node is an ancestor of the current root.
  621.    * @param aNode
  622.    *        The node to look for within the tree.
  623.    * @returns boolean
  624.    */
  625.   isInExistingRoot: function IOBox_isInExistingRoot(aNode)
  626.   {
  627.     let parentNode = aNode;
  628.     while (parentNode && parentNode != this.rootObject) {
  629.       parentNode = this.view.getParentObject(parentNode);
  630.     }
  631.     return parentNode == this.rootObject;
  632.   },
  633.  
  634.   /**
  635.    * Get the root node of a given node.
  636.    * @param aNode
  637.    *        The node whose root you wish to retrieve.
  638.    * @returns a root node or null
  639.    */
  640.   getRootNode: function IOBox_getRootNode(aNode)
  641.   {
  642.     let node = aNode;
  643.     let tmpNode;
  644.     while ((tmpNode = this.view.getParentObject(node)))
  645.       node = tmpNode;
  646.  
  647.     return node;
  648.   },
  649.  
  650.   /**
  651.    * Clean up our mess.
  652.    */
  653.   destroy: function IOBox_destroy()
  654.   {
  655.     delete this.view;
  656.     delete this.box;
  657.     delete this.rootObject;
  658.     delete this.rootObjectBox;
  659.     delete this.selectedObjectBox;
  660.     delete this.highlightedObjectBox;
  661.     delete this.scrollIntoView;
  662.   }
  663. };
  664.